﻿using Auxiliary.VecMath;
using Simple3DRedactor.Domain;
using Tao.OpenGl;

namespace Simple3DRedactor.OpenGLViewer
{
    interface IOpenGLObjectView : IObjectView
    {
        void Setup();

        void Render();

        void Move(Vector3D d);

        void Rotate(Vector3D d);

        bool Selected { get; set; }
    }

    abstract class ObjectView<T> : Domain.ObjectView<T>, IOpenGLObjectView where T : SceneObject
    {
        protected ObjectView(T obj) : base(obj)
        {
        }

        public abstract void Setup();

        public void Render()
        {
            if (!InnerObject.Active)
                return;
            
            PreRenderImpl();

            var needTranslate = InnerObject.Position != Vector3D.Zero;
            var needRotate = InnerObject.Rotation != Vector3D.Zero;

            if (needTranslate || needRotate)
            {
                Gl.glMatrixMode(Gl.GL_MODELVIEW);

                Gl.glPushMatrix();
            }

            if (needTranslate)
                Gl.glTranslatef(InnerObject.Position.X, InnerObject.Position.Y, InnerObject.Position.Z);

            if (needRotate)
            {
                Gl.glRotatef(InnerObject.Rotation.X, 1.0f, 0.0f, 0.0f);
                Gl.glRotatef(InnerObject.Rotation.Y, 0.0f, 1.0f, 0.0f);
                Gl.glRotatef(InnerObject.Rotation.Z, 0.0f, 0.0f, 1.0f);
            }

            RenderImpl();

            if (Selected)
            {
                Gl.glPushAttrib(Gl.GL_LIGHTING_BIT);
                Gl.glDisable(Gl.GL_LIGHTING);

                Gl.glColor3f(0.0f, 1.0f, 0.0f);

                Gl.glBegin(Gl.GL_LINES);
                    // Top
                    Gl.glVertex3f(MinPoint.X, MaxPoint.Y, MinPoint.Z);
                    Gl.glVertex3f(MaxPoint.X, MaxPoint.Y, MinPoint.Z);
                    Gl.glVertex3f(MaxPoint.X, MaxPoint.Y, MinPoint.Z);
                    Gl.glVertex3f(MaxPoint.X, MaxPoint.Y, MaxPoint.Z);
                    Gl.glVertex3f(MaxPoint.X, MaxPoint.Y, MaxPoint.Z);
                    Gl.glVertex3f(MinPoint.X, MaxPoint.Y, MaxPoint.Z);
                    Gl.glVertex3f(MinPoint.X, MaxPoint.Y, MaxPoint.Z);
                    Gl.glVertex3f(MinPoint.X, MaxPoint.Y, MinPoint.Z);

                    // Bottom
                    Gl.glVertex3f(MinPoint.X, MinPoint.Y, MinPoint.Z);
                    Gl.glVertex3f(MaxPoint.X, MinPoint.Y, MinPoint.Z);
                    Gl.glVertex3f(MaxPoint.X, MinPoint.Y, MinPoint.Z);
                    Gl.glVertex3f(MaxPoint.X, MinPoint.Y, MaxPoint.Z);
                    Gl.glVertex3f(MaxPoint.X, MinPoint.Y, MaxPoint.Z);
                    Gl.glVertex3f(MinPoint.X, MinPoint.Y, MaxPoint.Z);
                    Gl.glVertex3f(MinPoint.X, MinPoint.Y, MaxPoint.Z);
                    Gl.glVertex3f(MinPoint.X, MinPoint.Y, MinPoint.Z);

                    Gl.glVertex3f(MinPoint.X, MinPoint.Y, MinPoint.Z);
                    Gl.glVertex3f(MinPoint.X, MaxPoint.Y, MinPoint.Z);

                    Gl.glVertex3f(MaxPoint.X, MinPoint.Y, MinPoint.Z);
                    Gl.glVertex3f(MaxPoint.X, MaxPoint.Y, MinPoint.Z);

                    Gl.glVertex3f(MaxPoint.X, MinPoint.Y, MaxPoint.Z);
                    Gl.glVertex3f(MaxPoint.X, MaxPoint.Y, MaxPoint.Z);

                    Gl.glVertex3f(MinPoint.X, MinPoint.Y, MaxPoint.Z);
                    Gl.glVertex3f(MinPoint.X, MaxPoint.Y, MaxPoint.Z);
                Gl.glEnd();

                Gl.glPopAttrib();
            }

            if (needTranslate || needRotate)
                Gl.glPopMatrix();

            PostRenderImpl();
        }

        public void Move(Vector3D d)
        {
            InnerObject.Position += d;
        }

        public void Rotate(Vector3D d)
        {
            InnerObject.Rotation += d;
        }

        public bool Selected { get; set; }

        protected abstract void PreRenderImpl();
        protected abstract void RenderImpl();
        protected abstract void PostRenderImpl();

        protected abstract Vector3D MaxPoint { get; }
        protected abstract Vector3D MinPoint { get; }
    }
}
